home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
EC.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-09
|
9KB
|
360 lines
/* Driver for 3COM Ethernet card
* PC specific code
*/
#define TIMER 20000 /* Timeout on transmissions */
#include <stdio.h>
#include <dos.h>
#include "global.h"
#include "config.h"
#ifdef PC_EC
#include "mbuf.h"
#include "enet.h"
#include "iface.h"
#include "pktdrvr.h"
#include "netuser.h"
#include "ec.h"
#include "timer.h"
#include "arp.h"
#include "trace.h"
#include "pc.h"
#ifndef inportw /* previously in global.h */
#define inportw inport
#endif
static int near ec_init __ARGS((struct iface *iface,unsigned bufsize));
static int ec_raw __ARGS((struct iface *iface,struct mbuf *bp));
static int ec_stop __ARGS((struct iface *iface,int tmp));
static void near getecaddr __ARGS((unsigned base,char *cp));
static void near rcv_fixup __ARGS((unsigned base));
static void near setecaddr __ARGS((unsigned base,char *cp));
static INTERRUPT (*Ecvecsave[EC_MAX])();
static INTERRUPT (*Ecvec[])() = {ec0vec,ec1vec,ec2vec};
static struct ec Ec[EC_MAX]; /* Per-controller info */
static int Nec = 0;
/* Initialize interface */
static int near
ec_init(iface,bufsize)
struct iface *iface;
unsigned bufsize; /* Maximum size of receive queue in PACKETS */
{
int dev = iface->dev;
struct ec *ecp = &Ec[dev];
unsigned base = ecp->base;
ecp->iface = iface;
/* Pulse IE_RESET */
outportb(IE_CSR(base),IE_RESET);
/* Save old int vector */
Ecvecsave[dev] = getirq(ecp->vec);
/* Set interrupt vector */
if(setirq(ecp->vec,Ecvec[dev]) == -1){
tprintf("IRQ %u out of range\n",ecp->vec);
return -1;
}
maskon(ecp->vec); /* Enable interrupt */
if(iface->hwaddr == NULLCHAR)
iface->hwaddr = mxallocw(EADDR_LEN);
getecaddr(base,iface->hwaddr);
setecaddr(base,iface->hwaddr);
if(memcmp(iface->hwaddr,Ether_bdcst,EADDR_LEN) == 0){
tputs("EC address PROM contains broadcast address!!\n");
return -1;
}
/* Enable DMA/interrupt request, gain control of buffer */
outportb(IE_CSR(base),IE_RIDE|IE_SYSBFR);
/* Enable transmit interrupts */
outportb(EDLC_XMT(base),EDLC_16 | EDLC_JAM);
/* Set up the receiver interrupts and flush status */
outportb(EDLC_RCV(base),
EDLC_MULTI|EDLC_GOOD|EDLC_ANY|EDLC_SHORT|EDLC_DRIBBLE|EDLC_FCS|EDLC_OVER);
inportb(EDLC_RCV(base));
/* Start receiver */
outport(IE_RP(base),0); /* Reset read pointer */
outportb(IE_CSR(base),IE_RIDE|IE_RCVEDLC);
return 0;
}
/* Send raw packet (caller provides header) */
static int
ec_raw(iface,bp)
struct iface *iface; /* Pointer to interface control block */
struct mbuf *bp; /* Data field */
{
int i;
struct ec *ecp = &Ec[iface->dev];
unsigned base = ecp->base;
short size = len_p(bp);
dump(iface,IF_TRACE_OUT,CL_ETHERNET,bp);
ecp->estats.xmit++;
/* Pad the size out to the minimum, if necessary,
* with junk from the last packet (nice security hole here)
*/
if(size < RUNT)
size = RUNT;
size = (size+1) & ~1; /* round size up to next even number */
/* Wait for transmitter ready, if necessary. IE_XMTBSY is valid
* only in the transmit mode, hence the initial check.
*/
if((inportb(IE_CSR(base)) & IE_BUFCTL) == IE_XMTEDLC){
for(i = TIMER; (inportb(IE_CSR(base)) & IE_XMTBSY) && i != 0; i--)
;
if(i == 0) {
ecp->estats.timeout++;
free_p(bp);
return -1;
}
}
ecp->size = size;
/* Get control of the board buffer and disable receiver */
outportb(IE_CSR(base),IE_RIDE|IE_SYSBFR);
/* Point GP at beginning of packet */
outport(IE_GP(base),BFRSIZ-size);
/* Actually load each piece with a fast assembler routine */
while(bp != NULLBUF){
outbuf(IE_BFR(base),bp->data,bp->cnt);
bp = free_mbuf(bp);
}
/* Start transmitter */
outport(IE_GP(base),BFRSIZ-size);
outportb(IE_CSR(base),IE_RIDE|IE_XMTEDLC);
return 0;
}
/* Ethernet interrupt handler */
void
ecint(dev)
int dev;
{
struct mbuf *bp;
int16 size;
char stat;
struct phdr *phdr;
struct ec *ecp = &Ec[dev];
unsigned base = ecp->base;
ecp->estats.intrpt++;
/* Check for transmit jam */
if(!(inportb(IE_CSR(base)) & IE_XMTBSY)){
stat = inportb(EDLC_XMT(base));
if(stat & EDLC_16){
ecp->estats.jam16++;
rcv_fixup(base);
} else if(stat & EDLC_JAM){
/* Crank counter back to beginning and restart transmit */
ecp->estats.jam++;
outportb(IE_CSR(base),IE_RIDE|IE_SYSBFR);
outport(IE_GP(base),BFRSIZ - ecp->size);
outportb(IE_CSR(base),IE_RIDE|IE_XMTEDLC);
}
}
for(;;){
stat = inportb(EDLC_RCV(base));
if(stat & EDLC_STALE)
break;
if(stat & EDLC_OVER){
ecp->estats.over++;
rcv_fixup(base);
continue;
}
if(stat & (EDLC_SHORT | EDLC_FCS | EDLC_DRIBBLE)){
ecp->estats.bad++;
rcv_fixup(base);
continue;
}
if(stat & EDLC_ANY){
/* Get control of the buffer */
outport(IE_GP(base),0);
outportb(IE_CSR(base),IE_RIDE|IE_SYSBFR);
/* Allocate mbuf and copy the packet into it */
size = inportw(IE_RP(base));
if(size < RUNT || size > GIANT)
ecp->estats.bad++;
else if((bp = alloc_mbuf(size+sizeof(struct phdr))) == NULLBUF)
ecp->estats.nomem++;
else {
ecp->estats.recv++;
/* Generate descriptor header */
phdr = (struct phdr *)bp->data;
phdr->iface = ecp->iface;
phdr->type = CL_ETHERNET;
inbuf(IE_BFR(base),bp->data+sizeof(struct phdr),size);
bp->cnt = size + sizeof(struct phdr);
enqueue(&Hopper,bp);
}
outportb(IE_CSR(base),IE_RIDE|IE_RCVEDLC);
outportb(IE_RP(base),0);
}
}
/* Clear any spurious interrupts */
(void)inportb(EDLC_RCV(base));
(void)inportb(EDLC_XMT(base));
}
static void near
rcv_fixup(base)
unsigned base;
{
outportb(IE_CSR(base),IE_RIDE|IE_SYSBFR);
outportb(IE_CSR(base),IE_RIDE|IE_RCVEDLC);
outportb(IE_RP(base),0);
}
/* Read Ethernet address from controller PROM */
static void near
getecaddr(base,cp)
unsigned base;
char *cp;
{
int i;
for(i = 0; i < EADDR_LEN; i++) {
outport(IE_GP(base),i);
*cp++ = inportb(IE_SAPROM(base));
}
}
/* Set Ethernet address on controller */
static void near
setecaddr(base,cp)
unsigned base;
char *cp;
{
int i;
for(i = 0; i < EADDR_LEN; i++)
outportb(EDLC_ADDR(base)+i,*cp++);
}
/* Shut down the Ethernet controller */
static int
ec_stop(iface,tmp)
struct iface *iface;
int tmp;
{
int dev = iface->dev;
struct ec *ecp = &Ec[dev];
unsigned base = ecp->base;
/* Disable interrupt */
maskoff(Ec[dev].vec);
/* Restore original interrupt vector */
setirq(ecp->vec,Ecvecsave[dev]);
/* Pulse IE_RESET */
outportb(IE_CSR(base),IE_RESET);
outportb(IE_CSR(base),0);
return 0;
}
/* Attach a 3-Com model 3C500 Ethernet controller to the system
* argv[0]: hardware type, must be "3c500"
* argv[1]: I/O address, e.g., "0x300"
* argv[2]: vector, e.g., "3"
* argv[3]: mode, must be "arpa"
* argv[4]: interface label, e.g., "ec0"
* argv[5]: maximum number of packets allowed on receive queue, e.g., "5"
* argv[6]: maximum transmission unit, bytes, e.g., "1500"
* notused:
* argv[7]: IP address, optional (defaults to Ip_addr)
*/
int
ec_attach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct iface *if_ec;
int dev;
if(stricmp(argv[3],"arpa") != 0){
tputs("Mode must be arpa\n");
return -1;
}
if(if_lookup(argv[4]) != NULLIF) {
tprintf(Ifexist,argv[4]);
return -1;
}
if(Nec >= EC_MAX){
tprintf("Max %d Ethernet controller/s\n",EC_MAX);
return -1;
}
dev = Nec++;
if_ec = mxallocw(sizeof(struct iface));
if_ec->addr = Ip_addr;
setencap(if_ec, "Ethernet");
if_ec->name = strxdup(argv[4]);
if_ec->mtu = atoi(argv[6]);
if_ec->type = CL_ETHERNET;
if_ec->send = enet_send;
if_ec->output = enet_output;
if_ec->raw = ec_raw;
if_ec->stop = ec_stop;
if_ec->dev = dev;
Ec[dev].base = htoi(argv[1]);
Ec[dev].vec = htoi(argv[2]);
/* Initialize device */
if(ec_init(if_ec,(unsigned)atoi(argv[5])) != 0){
xfree(if_ec->name);
xfree(if_ec);
return -1;
}
if_ec->next = Ifaces;
Ifaces = if_ec;
if_ec->niface = Niface++;
return 0;
}
int
doetherstat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct ec *ecp;
char buf[20];
for(ecp = Ec;ecp < &Ec[Nec]; ecp++){
pether(buf,ecp->iface->hwaddr);
tprintf("Controller %u, Ethernet address %s\n",
(unsigned int)((ecp-Ec) / sizeof(struct ec)),buf);
tprintf("recv bad overf drop nomem intrpt\n"
"%-10lu%-10lu%-10lu%-10lu%-10lu%-10lu\n",
ecp->estats.recv,ecp->estats.bad,ecp->estats.over,
ecp->estats.drop,ecp->estats.nomem,ecp->estats.intrpt);
tprintf("xmit timeout jam jam16\n"
"%-10lu%-10lu%-10lu%-10lu\n",
ecp->estats.xmit,ecp->estats.timeout,ecp->estats.jam,
ecp->estats.jam16);
}
return 0;
}
#endif /* PC_EC */